package org.javahispano.javacup.tacticas_aceptadas.blacksmith;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;

public class TacticaAldeaGalaUtil {
	
	private static final double MIN_DISTANCIA_RIVAL = 2;
	private static final double DISTANCIA_A_PORTERO = 45;
	
	/**
	 * Devuelve cierto si alguna de las posiciones rivales se interponen entre las dos posiciones A y B
	 * @param posA posici�n A
	 * @param posB posici�n B
	 * @param posRival posiciones rivales
	 * @return cierto si alguna de las posiciones del rival se interponen entre la posici�n A y la posici�n B
	 */
	public static boolean rivalSeInterpone(Posicion posA, Posicion posB, Posicion[] posRival){
		
		// Se crea el resultado
		boolean seInterpone = false;
		
		for(int i=0; i<posRival.length && !seInterpone; i++){
			seInterpone = rivalSeInterpone(posA, posB, posRival[i]);
		}
		
		// Se devuelve el resultado
		return seInterpone;
		
	}
	
	/**
	 * Devuelve cierto si la posici�n del rival se interpone entre las dos posiciones A y B
	 * Se considera que el rival se interpone si est� a una distancia menor o igual a 
	 * MIN_DISTANCIA_RIVAL de la recta que une las posiciones A y B.
	 * Recta que pasa por dos puntos: x-x0/x1-x0 = y-y0/y1-y0
	 * Distancia de un punto a una recta: |ax + by + c| / sqr(a^2 + b^2)
	 * @param posA posici�n A
	 * @param posB posici�n B
	 * @param posRival posici�n del Rival
	 * @return cierto si la posici�n del rival se interpone entre la posici�n A y la posici�n B
	 */
	public static boolean rivalSeInterpone(Posicion posA, Posicion posB, Posicion posRival){
		
		// Coordenadas de A y B
		double x0 = posA.getX();
		double y0 = posA.getY();
		double x1 = posB.getX();
		double y1 = posB.getY();
		
		// Coordenadas rivales
		double x = posRival.getX();
		double y = posRival.getY();
		
		// Despejamos los valores a, b y c en las ecuaciones
		// x-x0/x1-x0 = y-y0/y1-y0
		// ax + by + c = 0
		double a = y1 - y0;
		double b = x0 - x1;
		double c = x1*y0 + y0*x0 - y1*x0 - x0*y0;
		
		// Se haya la distancia
		double dist = Math.abs(a*x + b*y + c) / Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
		
		// Se devuelve el resultado
		return dist<=MIN_DISTANCIA_RIVAL;
	}
	
	/**
	 * Devuelve los indices de los jugadores rivales que deben ser marcados ordenados 
	 * de m�s cercano a m�s lejamo. Un jugador rival debe ser marcado si se encuentra 
	 * a una distancia del portero menor o igual a DISTANCIA_A_PORTERO
	 * @param sp Situacion del partido
	 * @return Array con las posiciones de los rivales a marcar
	 */
	public static Posicion[] delanterosAMarcar(SituacionPartido sp){
		
		// Se crea la lista resultado
		ArrayList<Posicion> quienes = new ArrayList<Posicion>(11);
		
		for(int i=0;  i<11; i++){
			if(sp.rivales()[i].distancia(sp.misJugadores()[0])<=DISTANCIA_A_PORTERO){
				quienes.add(sp.rivales()[i]);
			}
		}
		
		// Se ordena a los rivales del mas cercano al mas lejano
		Collections.sort(quienes, new ComparadorRivalesCercanos());
		
		// Se devuelve el resultado
		Posicion[] tmp = new Posicion[quienes.size()];
        for (int i = 0; i < tmp.length; i++) {
            tmp[i] = quienes.get(i);
        }
        return tmp;
	}
	
	/**
	 * Devuelve cierto si ning�n rival est� cubriendo la porter�a
	 * @param sp Situaci�n del partido
	 * @return cierto si ning�n rival est� cubriendo la porter�a
	 */
	public static boolean estaPorteriaVacia(SituacionPartido sp){
		
		// Se crea el resultado
		boolean vacia = true;
		
		// Se obtienen los rivales que est�n cubriendo la porter�a
		for(int i=0; i<11 && vacia; i++){
			Posicion rival = sp.rivales()[i];
			if(rival.getX()>=-Constantes.LARGO_ARCO/2 && rival.getX()<=Constantes.LARGO_ARCO/2 && rival.getY()>=Constantes.LARGO_CAMPO_JUEGO/2-7){
				vacia = false;
			}
		}
		
		// Se devuelve el resultado
		return vacia;
		
	}	
	
	/**
	 * Decide la posici�n �ptima para un chute a gol en funci�n de la posici�n de los rivales
	 * @param sp Situaci�n del partido
	 * @return Posici�n destino �ptima del chute
	 */
	public static Posicion decidirDestinoChute(SituacionPartido sp){
		
		// Se crea el resultado
		Posicion destinoChute = Constantes.centroArcoSup;
		
		// Se obtienen los rivales que est�n en condiciones de parar un chute a gol
		// (aquellos situados bajo la porter�a)
		ArrayList<Posicion> rivales = new ArrayList<Posicion>(11);
		for(int i=0; i<11; i++){
			Posicion rival = sp.rivales()[i];
			if(rival.getX()>=-Constantes.LARGO_ARCO/2 && rival.getX()<=Constantes.LARGO_ARCO/2 && rival.getY()>=Constantes.LARGO_CAMPO_JUEGO/2-7){
				rivales.add(rival);
			}
		}
		
		// Si s�lo hay un rival (probablemente el portero) se dispara al lado que tenga m�s descubierto
		// (la distancia al palo es mayor)
		if(rivales.size()==1){
			Posicion portero = rivales.get(0);
			double distanciaAPaloIzqdo = portero.distancia(Constantes.posteIzqArcoSup);
			double distanciaAPaloDcho = portero.distancia(Constantes.posteDerArcoSup);
			if(distanciaAPaloIzqdo > distanciaAPaloDcho){
				destinoChute = new Posicion(-Constantes.LARGO_ARCO / 4, Constantes.LARGO_CAMPO_JUEGO / 2);
			}
			if(distanciaAPaloIzqdo < distanciaAPaloDcho){
				destinoChute = new Posicion(Constantes.LARGO_ARCO / 4, Constantes.LARGO_CAMPO_JUEGO / 2);
			}
		}
		
		// Para el resto de los casos (nadie cubre la porteria, el portero est� en el centro o hay
		// m�s de un rival cubriendo la porter�a), lo mejor es tirar al centro
		
		return destinoChute;
	}
	
    /**
     * Retorna la interseccion entre 2 rectas
     * @param n1 Punto 1 de la recta 1
     * @param n2 Punto 2 de la recta 2
     * @param m1 Punto 1 de la recta 2
     * @param m2 Punto 2 de la recta 2
     * @return Interseccion entre las dos rectas o null si son paralelas
     */
	public static Posicion Interseccion(Posicion n1, Posicion n2, Posicion m1, Posicion m2) {
        double a1 = n1.getY() - n2.getY();
        double b1 = n2.getX() - n1.getX();
        double c1 = n1.getX() * n2.getY() - n2.getX() * n1.getY();

        double a2 = m1.getY() - m2.getY();
        double b2 = m2.getX() - m1.getX();
        double c2 = m1.getX() * m2.getY() - m2.getX() * m1.getY();

        double d = a1 * b2 - a2 * b1;
        double d1 = c2 * b1 - c1 * b2;
        double d2 = a2 * c1 - a1 * c2;

        if (d != 0) {
            return new Posicion(d1 / d, d2 / d);
        } else {
            return null;
        }
    }
	
	private static class ComparadorRivalesCercanos implements Comparator<Posicion>{

		@Override
		public int compare(Posicion o1, Posicion o2) {
			Posicion centroPorteria = new Posicion(0, -Constantes.LARGO_CAMPO_JUEGO);
			return Double.compare(o1.distancia(centroPorteria), o2.distancia(centroPorteria));
		}
		
	}
	

}
